
import arrow
import datetime
from dateutil.parser import parse as dtparse
from flask import current_app

from app.db.models.users import User
from app.db.models.push_report import ReportPush
from app.report_push.data import ReportPushData
from app.log.logs import ReportPushLog
from app.libs.sendcloud import SendCloud
from app.libs.yunpian import YunPian
from app.libs.wechat import WeChat
from app.config.settings import SCANVHOST, DATETIME_FMT


NOT_PUSH_USERNAME = [
    "admin@knownsec.com"
]


def send_sms(user, report_url, phone):
    yunpian = YunPian()
    _, msg1, short_url_dict = yunpian.get_short_url(report_url)
    if short_url_dict and (short_url := short_url_dict.get("short_url", {}).get("short_url")):
        text = f"【ScanV】安全监测报告已生成，请及时查看资产安全情况。点击 {short_url} 查看报告详情。"
        _, msg2 = yunpian.normal_send(phone, text, v2=True)
        current_app.logger.info(f'{user.username} report push sms, {msg2}')
    else:
        current_app.logger.info(f'{user.username} report push sms short url fail, error: {msg1}')


def send_email(user, report_url, time_str, email, email_msg, email_msg0):
    send_cloud = SendCloud()
    args = {
        '%url%': [report_url],
        "%user%": [user.username],
        "%time%": [time_str],
        "%msg%": [email_msg],
        "%msg0%": [email_msg0],
    }
    args = {
        'to': [email],
        'sub': args
    }
    try:
        _, msg = send_cloud.sendmail("scanv_report_push", args)
        current_app.logger.info(f'{user.username} report push email, {msg}')
    except Exception as e:
        current_app.logger.info(f'{user.username} report push email fail, error: {str(e)}')


def send_wechat(user, time_str, report_url, wechat):
    wechat_obj = WeChat()
    wechat_param = {
        "keyword1": {"value": "ScanV"},
        "keyword2": {"value": arrow.now().strftime('%Y-%m-%d %H:%M:%S')},
        "keyword3": {"value": "安全监测报告"},
        "keyword4": {"value": f"【{time_str}】报告已经生成，点击查看详情"}
    }
    try:
        resp = wechat_obj.send_tpl_msg("Iqwp3-LMT9Eq2GjOb8bD3v68hsFDvgGaawp1mQ05Ny4", wechat, report_url, wechat_param)
        current_app.logger.info(f'{user.username} report push webchat, {resp}')
    except Exception as e:
        current_app.logger.info(f'{user.username} report push wechat fail, error: {str(e)}')


def get_email_msg(result, enable_balance):
    time_str = f'{result["start_time"]}~{result["end_time"]}'
    overview_data = result.get("overview_data", {})

    sec_task_count = result.get("web_sec_task_data", {}).get("web_sec_scan_count", 0)
    critical_asset_count = result.get("asset_risk_data", {}).get("critical", 0)
    high_asset_count = result.get("asset_risk_data", {}).get("high", 0)
    medium_asset_count = result.get("asset_risk_data", {}).get("medium", 0)
    low_asset_count = result.get("asset_risk_data", {}).get("low", 0)

    ava_task_count = sum([overview_data.get("completed_task_count", {}).get(t, 0)for t in ReportPushData.available_type])
    available_risk_asset_count = result.get("asset_status_data", {}).get("available_risk_asset_count", 0)
    available_safe_asset_count = result.get("asset_status_data", {}).get("available_safe_asset_count", 0)

    completed_task_type = result.get("completed_task_type", {})
    completed_sec_task = {t: v for t, v in completed_task_type.items() if t in ReportPushData.web_sec_type}
    enable_task_map = result.get("enable_task_map", {})
    good_sec_task_map = result.get("web_sec_task_data", {}).get("good_sec_task_map", {})
    risk_count = result.get("web_sec_task_data", {}).get("risk_count", 0)
    error_sec_task_count = result.get("web_sec_task_data", {}).get("error_sec_task_count", 0)
    msg_0 = ""
    vul_str = "未发现威胁"
    vul_str_list = []
    if critical_asset_count:
        vul_str_list.append(f"严重资产{critical_asset_count}个")
    if high_asset_count:
        vul_str_list.append(f"高危资产{high_asset_count}个")
    if medium_asset_count:
        vul_str_list.append(f"中危资产{medium_asset_count}个")
    if low_asset_count:
        vul_str_list.append(f"低危资产{low_asset_count}个")
    if vul_str_list:
        vul_str = f"其中{'，'.join(vul_str_list[:2])}"
    # 场景1 开通了套餐服务， 开启了监测，监测任务正常执行
    msg = f"【{time_str}】周期内ScanV对资产进行Web安全监测共{sec_task_count}次，{vul_str}；可用性监测共{ava_task_count}次，其中访问正常资产{available_safe_asset_count}个，访问异常资产{available_risk_asset_count}个。"
    # 场景2 开通了套餐服务， 开启了监测，web安全监测任务都异常且无漏洞数据
    if all(enable_balance.values()) and any(completed_sec_task.values()) and (not risk_count) and (not any(good_sec_task_map.values())):
        msg = f"【{time_str}】周期内ScanV对资产进行Web安全监测共{sec_task_count}次，异常监测{error_sec_task_count}次；可用性监测共{ava_task_count}次，其中访问正常资产{available_safe_asset_count}个，访问异常资产{available_risk_asset_count}个。请立即前往控制台查看详情。"
    # 场景4 当前未开启监测， 统计周期内无任务执行
    elif all(enable_balance.values()) and (not any(enable_task_map.values())) and (not any(completed_task_type.values())):
        msg = f"【{time_str}】周期内ScanV对资产进行Web安全监测共0次，可用性监测共0次。当前未开启监测，请立即前往控制台下发监测任务。"
    # 场景6 web安全开通，可用性未开通
    elif enable_balance.get("web_sec") and (not enable_balance.get("web_ava")):
        msg = f"【{time_str}】周期内ScanV对资产进行Web安全监测共{sec_task_count}次，{vul_str}。"
        msg_0 = "未进行可用性监测，建议您购买可用性监测服务，实时掌控资产可用性状态。"
    # 场景6 web安全未开通，可用性开通
    elif enable_balance.get("web_ava") and (not enable_balance.get("web_sec")):
        msg = f"【{time_str}】周期内ScanV对资产进行可用性监测共{ava_task_count}次，其中访问正常资产{available_safe_asset_count}个，访问异常资产{available_risk_asset_count}个。"
        msg_0 = "未进行Web安全监测，建议您购买Web安全监测服务，未您提供全方位安全监测服务。"
    return msg, msg_0


def report_push(end_time=None, start_time=None, usernames: list = None,
                phone: list = None, email: list = None, wechat: list = None,
                default_receiver=True):
    end_time = end_time or arrow.utcnow().datetime
    ago_7 = (arrow.get(end_time).to("local").datetime + datetime.timedelta(days=-6)).replace(
        hour=0, minute=0, second=0, microsecond=0)
    start_time = start_time or arrow.get(dtparse(f"{ago_7.strftime(DATETIME_FMT)} CST")).to("utc").datetime
    kb_data = {}
    base_report_url = f"{SCANVHOST}/#/report/task_report/?id="
    user_q = {"username__nin": NOT_PUSH_USERNAME}
    if usernames:
        user_q = {"username__in": usernames}
    for user in User.objects.filter(**user_q):
        current_app.logger.info(f"{user.username} push report start")
        report_obj = ReportPushData(
            user=user,
            start_time=start_time,
            end_time=end_time,
            kb_data=kb_data
        )
        if not kb_data:
            kb_data = report_obj.get_kb_data(kb_data)
        try:
            is_push_report, result, report_msg = report_obj.get_data()
        except Exception as e:
            current_app.logger.info(f'{user.username} report push get data fail, {str(e)}')
            continue
        if not is_push_report:
            current_app.logger.info(f'{user.username} report push is_push_report {is_push_report} {result} {report_msg}')
            continue
        receiver_data = result.get("receiver_data") or {}
        if not default_receiver:
            receiver_data.update({
                "phone": phone or [],
                "email": email or [],
                "wechat": wechat or [],
            })
        if not any(receiver_data.values()):
            current_app.logger.info(f'{user.username} report push is_push_report false not receiver data')
            continue
        current_app.logger.info(f'{user.username} report push receiver_data {receiver_data}')
        enable_balance = result.get("overview_data", {}).get("enable_balance", {})
        email_msg, email_msg0 = get_email_msg(result, enable_balance)
        res = ReportPush._get_collection().insert_one(result)
        report_url = f"{base_report_url}{res.inserted_id}"
        time_str = f'{result["start_time"]}~{result["end_time"]}'
        for phone in receiver_data.get("phone", []):
            send_sms(user, report_url, phone)
        for email in receiver_data.get("email", []):
            send_email(user, report_url, time_str, email, email_msg, email_msg0)
        for wechat in receiver_data.get("wechat", []):
            send_wechat(user, time_str, report_url, wechat)
        current_app.logger.info(f"{user.username} push report completed: {res.inserted_id}")
        if not default_receiver:
            continue
        log_info = {
            'affects': [],
            'actionMessage': '报告推送',
            'detail': "系统推送报告",
            "uid": str(user.id),
            "operator": "admin@knownsec.com",
            "logSource": "back",
            "log_type": "ops"
        }
        ReportPushLog(**log_info).info(log_info['actionMessage'], log_info['detail'])
